package com.stay.emoticon;

import java.util.ArrayList;
import java.util.HashMap;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Movie;
import android.graphics.Paint;
import android.graphics.PointF;
import android.util.Log;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.BaseAdapter;
import android.widget.FrameLayout;
import android.widget.GridView;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.Scroller;
import android.widget.Toast;

/**
 * @Description: EmoticonView <h1>you can only new {@link EmoticonView},implements a {@link OnEmoticonTapListener} to get the
 *               drawalbeId of emoticon</h1> <h1>sample:</h1>
 * 
 *               <pre class="prettyprint">
 * public void onEmoticonTap(int drawableId) {
 * 	ImageGetter imageGetter = new ImageGetter() {
 * 		public Drawable getDrawable(String source) {
 * 			int id = Integer.parseInt(source);
 * 			Drawable d = getResources().getDrawable(id);
 * 			d.setBounds(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());
 * 			return d;
 * 		}
 * 	};
 * 	CharSequence cs1 = Html.fromHtml(&quot;&lt;img src='&quot; + drawableId + &quot;'/&gt;&quot;, imageGetter, null);
 * 	int index = et.getSelectionStart();
 * 	Editable etb = et.getText();
 * 	int length = etb.length();
 * 	if (index &lt; length) {
 * 		CharSequence cs = etb.subSequence(0, length);
 * 		CharSequence cs2 = cs.subSequence(0, index);
 * 		CharSequence cs3 = cs.subSequence(index, length);
 * 		edittext.setText(null);
 * 		edittext.append(cs2);
 * 		edittext.append(cs1);
 * 		edittext.append(cs3);
 * 	} else {
 * 		edittext.append(cs1);
 * 	}
 * 	edittext.setSelection(index + 1);
 * }
 * </pre>
 * 
 *               <h2>attention:<h2>
 *               you should setText as CharSequence only that you can display a image in textview or edittext
 * 
 *               <pre class="prettyprint">
 * Editable etb = edittext.getText();
 * CharSequence cs = etb.subSequence(0, etb.length());
 * </pre>
 * @author Stay
 * 
 * @Date 2011-12-7 05:04:07
 * 
 */
public class EmoticonView extends LinearLayout {
	private Context context;
	private ScrollerView emoticon;
	private LinearLayout indexLayout;
	private int curPositon = 0;
	private OnEmoticonTapListener mOnEmoticonTap;
	private float density;
	private PaintTest paint;
	private static final int NUMCOLUMNS = 7;
	private static final int GRIDCOUNT = 28;
	private static final int GALLERYCOUNT = 3;
	private static final int COLUMNWIDTH = 40;
	private static final int VERTICALSPACING = 5;
	private static final int PADDING = 10;
	public static final int STARTEMOTICON = R.drawable.mr_100;
	public static final int ENDEMOTICON = R.drawable.mr_182;
	public static final int EMOTICONSQUARE = 30;
	public static final int RADIUS = 2;

	public EmoticonView(Context context, OnEmoticonTapListener onEmoticonTap) {
		super(context);
		this.context = context;
		this.setOrientation(VERTICAL);
		this.setBackgroundColor(Color.WHITE);
		density = this.getResources().getDisplayMetrics().density;
		this.mOnEmoticonTap = onEmoticonTap;
		emoticon = new ScrollerView();
		this.addView(emoticon);
		initPositionView();
	}

	private void initPositionView() {// initial position view to flag the page of emoticons
		indexLayout = new LinearLayout(context);
		indexLayout.setVisibility(View.VISIBLE);
		LayoutParams lp = new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT);
		lp.setMargins(0, (int) (density * PADDING), 0, (int) (density * PADDING));
		indexLayout.setLayoutParams(lp);
		indexLayout.setGravity(Gravity.CENTER);
		paint = new PaintTest(context);
		indexLayout.addView(paint);
		this.addView(indexLayout);
	}
	
	@Override
	protected void onVisibilityChanged(View changedView, int visibility) {
		super.onVisibilityChanged(changedView, visibility);
		if (visibility == View.VISIBLE) {
			emoticon.scrollTo(0, 0);
			emoticon.invalidate();
		}
	}
	
	@Override
	public boolean onKeyDown(int keyCode, KeyEvent event) {
		if (event.getKeyCode() == KeyEvent.KEYCODE_BACK && getVisibility() == View.VISIBLE) {
			setVisibility(View.GONE);
			return true;
		}
		return false;
	}
	
	/**
	 * @Description:when emotion position change ,setActiveDrawable to flag the page have been change
	 * 
	 * @param index
	 * 
	 * @return void
	 */
	public void onEmoticonChange(int index) {
		if (curPositon == index) {
			return;
		}
		this.curPositon = index;
		paint.setPos(index);
		paint.invalidate();
	}

	/**
	 * @Description:emoticon view, support slide over,one emoticon click
	 * 
	 * @author Stay
	 * 
	 * @Date 2011-12-7 05:03:11
	 * 
	 */
	public class ScrollerView extends FrameLayout {
		private Scroller scroller;
		private String TAG = "ScrollerView";
		// private Drawable drawable;
		private PointF last = new PointF();
//		private final int TOUCH_SLOP = ViewConfiguration.get(getContext()).getScaledTouchSlop() - 10;
		private final int TOUCH_SLOP = 5;
		private HashMap<Integer, ArrayList<Integer>> emoticons = new HashMap<Integer, ArrayList<Integer>>();
		private boolean isLeft;

		public ScrollerView() {
			super(context);
			scroller = new Scroller(context);
			initEmoticon();
			Log.d("Emoticon", "TOUCH_SLOP="+TOUCH_SLOP);
		}

		private void initEmoticon() {
			ArrayList<Integer> temp = null;
			for (int i = 0; i < GALLERYCOUNT; i++) {
				temp = new ArrayList<Integer>();
				for (int j = 0; j < GRIDCOUNT; j++) {
					if (STARTEMOTICON + j + GRIDCOUNT * i > ENDEMOTICON) {
						break;
					}
					temp.add(STARTEMOTICON + j + GRIDCOUNT * i);
				}
				emoticons.put(i, temp);
			}
			for (int i = 0; i < GALLERYCOUNT; i++) {
				GridView gridView = new GridView(context);
				gridView.setNumColumns(NUMCOLUMNS);
				gridView.setStretchMode(GridView.STRETCH_COLUMN_WIDTH);
				gridView.setColumnWidth((int) (density * COLUMNWIDTH));
				gridView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,
						ViewGroup.LayoutParams.WRAP_CONTENT));
				gridView.setVerticalSpacing((int) (density * VERTICALSPACING));
				gridView.setGravity(Gravity.CENTER);
				gridView.setPadding((int) (density * PADDING), (int) (density * PADDING),
						(int) (density * PADDING), 0);
				gridView.setOnItemClickListener(new OnItemClickListener() {
					@Override
					public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
						mOnEmoticonTap.onEmoticonTap(Integer.parseInt(parent.getItemAtPosition(
								position).toString()));
					}
				});
				gridView.setAdapter(new GridAdpater(emoticons.get(i)));
				this.addView(gridView);
			}
		}
		
		@Override
		public boolean onInterceptTouchEvent(MotionEvent event) {
			Log.d(TAG, "onInterceptTouchEvent ");
			final int x = (int) event.getX();
			boolean flag = false;
			switch (event.getAction()) {
			case MotionEvent.ACTION_DOWN:
				Log.d(TAG, "onInterceptTouchEvent ACTION_DOWN");
				last.x = x;
				break;
			case MotionEvent.ACTION_MOVE:
				Log.d(TAG, "onInterceptTouchEvent ACTION_MOVE");
				final int deltaX = (int) (last.x - x);
				if (Math.abs(deltaX) > TOUCH_SLOP) {
					flag = true;
				}
				break;
			case MotionEvent.ACTION_UP:
				Log.d(TAG, "onInterceptTouchEvent ACTION_UP");
				break;
			}
			return flag;
		}
		

		public boolean onTouchEvent(MotionEvent event) {
			final int x = (int) event.getX();
			final int width = getWidth();

			switch (event.getAction()) {
			case MotionEvent.ACTION_DOWN:
				Log.d(TAG, "onTouchEvent ACTION_DOWN");
				if (!scroller.isFinished()) {
					scroller.abortAnimation();
				}

				break;
			case MotionEvent.ACTION_MOVE:
				Log.d(TAG, "onTouchEvent ACTION_MOVE");
				final int deltaX = (int) (last.x - x);
				if (Math.abs(deltaX) < TOUCH_SLOP) {
					break;
				}
				last.x = x;
				if (deltaX < 0) {
					isLeft = true;
					if (getScrollX() > 0) {
						scrollBy(Math.max(-getScrollX(), deltaX), 0);
					}
				} else if (deltaX > 0) {
					isLeft = false;
					final int availableToScroll = getChildAt(getChildCount() - 1).getRight()
							- getScrollX() - getWidth();
					if (availableToScroll > 0) {
						scrollBy(Math.min(availableToScroll, deltaX), 0);
					}
				}

				break;
			case MotionEvent.ACTION_UP:
			case MotionEvent.ACTION_CANCEL:
				Log.d(TAG, "onTouchEvent ACTION_UP");
				int dx = 0;
				if(isLeft){
					dx = (getScrollX() + width / 6) / width;
				}else {
					dx = (getScrollX() + width *5/ 6) / width;
				}
				if (dx < 0) {
					dx = 0;
				}
				if (dx > getChildCount() - 1) {
					dx = getChildCount() - 1;
				}
				dx *= width;
				dx -= getScrollX();
				scroller.startScroll(getScrollX(), 0, dx, 0, Math.abs(dx) * 3);
				break;
			}
			invalidate();
			return true;
		}

		@Override
		protected void onLayout(boolean changed, int l, int t, int r, int b) {
//			Log.d(TAG, "onLayout---changed" + changed + ",l=" + l + ",t=" + t + ",r=" + r + ",b=" + b);
			final int width = getWidth();
			final int count = getChildCount();
			int height = getHeight();
			int childLeft = 0;
			for (int i = 0; i < count; i++) {
				final View child = getChildAt(i);
				child.layout(childLeft, 0, childLeft + width, height);
				childLeft += width;
			}
		}

		@Override
		protected void dispatchDraw(Canvas canvas) {
			Log.d(TAG, "dispatchDraw----x=" + getScrollX() + "y=" + getScrollY() + "width=" + getWidth()
					+ ",height=" + getHeight());
			// if (null != drawable) {
			// drawable.setBounds(0, 0, getChildCount() * getWidth(), getHeight());
			// drawable.draw(canvas);
			// }
			if (getScrollX() % getWidth() == 0) {
				onEmoticonChange(getScrollX() / getWidth());
			}
			super.dispatchDraw(canvas);
		}

		@Override
		public void computeScroll() {
			Log.d(TAG, "computeScroll" + scroller.getCurrX() + ",y=" + scroller.getCurrY());
			if (scroller.computeScrollOffset()) {
				scrollTo(scroller.getCurrX(), scroller.getCurrY());
				invalidate();
			}
		}

		class GridAdpater extends BaseAdapter {
			ArrayList<Integer> drawalbes = null;
			private ViewHolder holder;

			public GridAdpater(ArrayList<Integer> resID) {
				this.drawalbes = resID;
			}

			public int getCount() {
				return drawalbes.size();
			}

			public Object getItem(int position) {
				return drawalbes.get(position);
			}

			public long getItemId(int position) {
				return position;
			}

			public View getView(int position, View convertView, ViewGroup parent) {
				if (convertView == null || convertView.getTag() == null) {
					holder = new ViewHolder();
					LinearLayout layout = new LinearLayout(context);
					layout.setGravity(Gravity.CENTER);
					if (false) {//  if your phone has low ram ,try to use imageview, the gif image need more ram to decode
						holder.gif = new GifImageView(context);
						holder.gif.setLayoutParams(new LayoutParams(
								(int) (density * EMOTICONSQUARE),
								(int) (density * EMOTICONSQUARE)));
						layout.addView(holder.gif);
						convertView = layout;
					}else {
						holder.emoticon = new ImageView(context);
						holder.emoticon.setLayoutParams(new LayoutParams(
								(int) (density * EMOTICONSQUARE),
								(int) (density * EMOTICONSQUARE)));
						layout.addView(holder.emoticon);
						convertView = layout;
					}
					convertView.setTag(holder);
				} else {
					holder = (ViewHolder) convertView.getTag();
				}
				if (false) {
					holder.gif.setGifResource(drawalbes.get(position));
				}else {
					holder.emoticon.setImageResource(drawalbes.get(position));
				}
				return convertView;
			}
		}
	}

	static class ViewHolder {
		public ImageView emoticon;
		GifImageView gif;
	}

	public interface OnEmoticonTapListener {
		void onEmoticonTap(int drawableId);
	}

	public class PaintTest extends View {
		private int mPosition;
		private int mDisWidth;
		private Paint p;
		private int mInitX = 0;

		public PaintTest(Context context) {
			super(context);
			LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT,
					(int) (RADIUS * density) * 2);
			lp.gravity = Gravity.CENTER;
			mDisWidth = context.getResources().getDisplayMetrics().widthPixels;
			mInitX = (mDisWidth - (GALLERYCOUNT - 1) * (int) (density * PADDING)) / 2;
			this.setLayoutParams(lp);
		}

		public void setPos(int mPosition) {
			this.mPosition = mPosition;
		}

		@Override
		protected void onDraw(Canvas canvas) {
			super.onDraw(canvas);
			p = new Paint();
			for (int i = 0; i < GALLERYCOUNT; i++) {
				if (i == mPosition) {
					p.setColor(Color.BLUE);
					canvas.drawCircle(i * (int) (density * PADDING) + mInitX,
							(int) (RADIUS * density), (int) (RADIUS * density), p);
				} else {
					p.setColor(Color.GRAY);
					canvas.drawCircle(i * (int) (density * PADDING) + mInitX,
							(int) (RADIUS * density), (int) (RADIUS * density), p);
				}
			}
		}
	}

	/**
	 * @Description: support gif view
	 *
	 * @author Stay
	 *
	 * @Date 2011-12-12 02:48:36 pm
	 * 
	 */
	class GifImageView extends ImageView {
		private long mMovieStart;
		private Movie mMovie;

		public GifImageView(Context context) {
			super(context);
		}

		public void setGifResource(int drawableId) {
			mMovie = Movie.decodeStream(context.getResources().openRawResource(drawableId));
		}

		@Override
		protected void onDraw(Canvas canvas) {
			super.onDraw(canvas);
			long now = android.os.SystemClock.uptimeMillis();
			if (mMovieStart == 0) { // first time
				mMovieStart = now;
			}
			if (mMovie != null) {
				int dur = mMovie.duration();
				if (dur == 0) {
					dur = 1000;
				}
				int relTime = (int) ((now - mMovieStart) % dur);
				mMovie.setTime(relTime);
				mMovie.draw(canvas, (getWidth() - mMovie.width())/2, (getHeight() - mMovie.height())/2);
				invalidate();
			}
		}

	}
}
